﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Nexterday.Linq.Autocompiler;
using System.Collections.Specialized;
using LinqKit;
using System.IO;
using System.Text.RegularExpressions;
using Nexterday.Linq.Autocompiler.L2S;


namespace Nexterday.Linq.Autocompiler.L2S.Tests
{
    [TestFixture]
    public class QueryTests
    {
        //[Test]
        public void Querying_By_Id_Twice_In_The_Same_Context_Only_Hits_The_Database_Once()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                using (var writer = new StringWriter())
                {
                    db.Log = writer;
                    var product = db.Products.AsCompilable().Single(x => x.ProductID == 1);
                    var product2 = db.Products.AsCompilable().Single(x => x.ProductID == 1);
                    var selectCount = Regex.Matches(writer.ToString(), "SELECT").Count;
                    Assert.AreEqual(1, selectCount);
                }
            }
        }

        [Test]
        public void Can_Select_Single_Product()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                var product = (from p in db.Products.AsCompilable() where p.ProductID == 1 select p).Single();
                Assert.AreEqual(1, product.ProductID);
            }
        }

        [Test]
        public void Executes_Same_Sql_Query_As_Standard_Provider()
        {
            string sql1, sql2;
            using (StringWriter sw = new StringWriter())
            {
                using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
                {
                    db.Log = sw;
                    var product = (from p in db.Products where p.ProductID == 1 select p).Single();
                    Assert.AreEqual(1, product.ProductID);
                }
                sql1 = sw.ToString();
            }
            using (StringWriter sw = new StringWriter())
            {
                using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
                {
                    db.Log = sw;
                    var product = (from p in db.Products.AsCompilable() where p.ProductID == 1 select p).Single();
                    Assert.AreEqual(1, product.ProductID);
                }
                sql2 = sw.ToString();
            }

            Assert.AreEqual(sql1, sql2);
        }

        [Test]
        public void Can_Select_Top_5_Products_Ordered_By_ProductID()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                var products = db.Products.AsCompilable().OrderBy(x => x.ProductID).Take(5).ToList();
                Assert.AreEqual(5, products.Count());
            }
        }

        [Test]
        public void Can_Select_Products_Ordered_By_ProductID_Using_Linq_Syntax()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                var products = (from t in db.Products.AsCompilable() orderby t.ProductID select t).ToList();
                Assert.AreEqual(1, products.First().ProductID);
                Assert.AreEqual(2, products.Skip(1).First().ProductID);
            }
        }

        [Test]
        public void Can_Select_Two_Different_Types_With_Equally_Named_Properties()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                Assert.DoesNotThrow(() =>
                {
                    var products = db.Customers.AsCompilable().Select(x => x.Orders.First());
                    var categories = db.Employees.AsCompilable().Select(x => x.Orders.First());
                });
            }
        }

        [Test]
        public void Can_Select_Projection_To_Anonymous_Class()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                Assert.DoesNotThrow(() =>
                {
                    var products = from c in db.Customers.AsCompilable()
                                   select new { c.City, c.Phone };
                    var results = products.ToList();
                });
            }
        }

        [Test]
        public void Can_Select_Projection_To_Existing_Class()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                Assert.DoesNotThrow(() =>
                {
                    var products = from c in db.Customers.AsCompilable()
                                   select new FileInfo(c.CompanyName);
                    var results = products.ToList();
                });
            }
        }

        [Test]
        public void Can_Select_Single_Product_Based_On_Parameter()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                int productId = 1;
                var product = (from p in db.Products.AsCompilable()
                               where p.ProductID == productId
                               select p).Single();
                Assert.AreEqual(1, product.ProductID);
            }
        }

        [Test]
        public void Can_Select_Single_Product_Based_On_FuncCall()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                Func<int> productId = () => 1;
                var product = (from p in db.Products.AsCompilable()
                               where p.ProductID == productId.Invoke()
                               select p).Single();
                Assert.AreEqual(1, product.ProductID);
            }
        }

        private int GetProductId()
        {
            return 1;
        }

        [Test]
        public void Can_Select_Single_Product_Based_On_Local_Method()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                Func<int> productId = () => 1;
                var product = (from p in db.Products.AsCompilable()
                               where p.ProductID == GetProductId()
                               select p).Single();
                Assert.AreEqual(1, product.ProductID);
            }
        }

        [Test]
        public void Can_Select_Joined()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                var orders = (from p in db.Products.AsCompilable()
                              from c in p.Order_Details
                              select c.Order).Distinct();
                Assert.AreEqual(830, orders.Count());
            }
        }



        [Test]
        public void Can_Select_Single_Product_Based_On_Value_In_NameValueCollection()
        {
            NameValueCollection col = new NameValueCollection();
            col["ProductId"] = "1";
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                var product = (from p in db.Products.AsCompilable()
                               where p.ProductID == int.Parse(col["ProductId"])
                               select p).Single();
                Assert.AreEqual(1, product.ProductID);
            }
        }

        [Test]
        public void Can_Select_Single_Product_Based_On_Many_Parameters()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                int productId = 1;
                int productId2 = -1;
                int productId3 = -1;
                int productId4 = -1;
                int productId5 = -1;
                var product = (from p in db.Products.AsCompilable()
                               where p.ProductID == productId ||
                               p.ProductID == productId2 ||
                               p.ProductID == productId3 ||
                               p.ProductID == productId4 ||
                               p.ProductID == productId5
                               select p).Single();
                Assert.AreEqual(1, product.ProductID);
            }
        }

        [Test]
        public void Cant_Select_Products_Based_On_Array_Of_Parameters_When_SupportEnumerableParameters_Is_False()
        {
            Assert.Throws(typeof(NotSupportedException), () =>
            {
                using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
                {
                    var productIds = Enumerable.Range(1, 5);

                    var count = (from p in db.Products.AsCompilable(new CompilationParameters
                    {
                        SupportEnumerableParameters = false
                    })
                                 where productIds.Contains(p.ProductID)
                                 select p).Count();

                }
            });
        }

        [Test]
        public void Can_Select_Products_Based_On_Array_Of_Parameters_When_SupportEnumerableParameters_Is_True()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                var productIds = Enumerable.Range(1, 5);

                var count = (from p in db.Products.AsCompilable(new CompilationParameters
                {
                    SupportEnumerableParameters = true
                })
                             where productIds.Contains(p.ProductID)
                             select p).Count();
                Assert.AreEqual(5, count);
            }
        }


        [Test]
        public void Can_Select_Category_Based_On_Product_Parameter()
        {
            using (NorthwindExpressDataContext db = new NorthwindExpressDataContext())
            {
                var product = db.Products.First();
                var categories = (from p in db.Categories.AsCompilable()
                                  where p.Products.Any(x => x == product)
                                  select p).ToList();
                Assert.That(categories.Count == 1);
            }
        }
    }
}
